Este notebook trata de explicar paso a paso el recorrido e interpretacion de la prueba y explicar cada punto con el punto de vista y el codigo necesario que generé para esta interpretacion. Las graficas fueron generadas con ploty por lo que se requiere ir ejecutandolas para poder visualizarlas ya que no quedan precargadas
import pandas as pd
import plotly.express as px
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
from datetime import timedelta
df = pd.read_csv('data_test (1).csv', sep = ';')
# Se seleccionan solo las transacciones validas que me generan una fecha
df = df[df.status == 'PAID']
# Cambio de la estructura de la fecha
df['paidAt'] = pd.to_datetime(df['paidAt'])
#df['paidAt'] = df['paidAt'].fillna('0/0/0 00:00', inplace=True) # Cambio si necesito realizar analisis sobre transacciones en proceso
# Genero la variable mes
df['mes_id'] = df['paidAt'].dt.strftime('%Y-%m')
# Calculo del porcentaje de financiación
df['finan'] = df.amountfinancedByXepelin/df.amount
# Generacion de la tabla maestra con los cambios
df.head(5)
| PayerId | ReceiverId | invoiceId | paidAt | amount | amountfinancedByXepelin | status | mes_id | finan | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 53 | 10 | 18660729 | 2021-08-23 | 1490.46 | 0.00 | PAID | 2021-08 | 0.000000 |
| 1 | 93 | 11 | 18660730 | 2021-03-22 | 6418.28 | 624.48 | PAID | 2021-03 | 0.097297 |
| 2 | 122 | 12 | 18660731 | 2021-02-18 | 27979.20 | 10520.15 | PAID | 2021-02 | 0.375999 |
| 3 | 85 | 13 | 18660732 | 2021-03-15 | 183070.77 | 79421.63 | PAID | 2021-03 | 0.433830 |
| 4 | 87 | 14 | 18660733 | 2021-07-23 | 20532.00 | 20532.00 | PAID | 2021-07 | 1.000000 |
# Porcentaje de financiacion del total transado en la plataforma
fin_mounth = df.groupby('mes_id').agg({'amountfinancedByXepelin':[sum],'amount':[sum]})
fin_mounth.columns = fin_mounth.columns.droplevel(level=1)
fin_mounth = fin_mounth.reset_index()
fin_mounth['finan'] = fin_mounth.amountfinancedByXepelin / fin_mounth.amount
# Grafico relacion vs Financiación
#fig = go.Figure()
fig = make_subplots(specs=[[{"secondary_y": True}]])
fig.add_trace(go.Bar(
x=fin_mounth['mes_id'],
y=fin_mounth['amount'],
name='Monto total transado',
marker_color='blue'
))
fig.add_trace(go.Scatter(x=fin_mounth['mes_id'], y=fin_mounth['finan'],
mode='lines',
name='Porcentaje de Financiacion' ),
secondary_y= True,
)
fig.update_layout(
title_text="Relación monto vs Financiación"
)
fig.show()
Como vemos en la siguiente grafica en cada mes el % de financiacion de un producto puede llegar a variar oscilando en meses de 0.78 y 0.4 aproximadamen indicandome algunos meses con un potencial de crecimiento en la financiacón de los clientes
# Montos financiados por mes
fin_mounth = df.groupby('mes_id')['amountfinancedByXepelin'].sum().reset_index()
fig = px.bar(fin_mounth, x = 'mes_id', y = 'amountfinancedByXepelin' )
fig.show()
Debido a que la inversion varia es diferente el consolidado de informacion de financiamiento que estoy generando en la plataforma encontrando unos meses minimos o nulos y unos con mayor exito
# Total financiado por cliente
client = df.groupby('PayerId')['amountfinancedByXepelin'].sum().reset_index()
fig = px.bar(client, x = 'PayerId', y = 'amountfinancedByXepelin')
fig.show()
La Anterior relacion nos ayuda a entender del total de empresas como se esta presetando la financiación y cual es la deuda generada motivo de la financiacion para cada cliente
La siguiente tabla nos ayuda a entender un poco la caracterización de las transacciones y revisar como es la frecuencia de usso de cada cliente su % de financiacion total y el porcentaje de crecimiento
# Estadisticos historicos de clientes
client = df.groupby('PayerId').agg({'amountfinancedByXepelin':[sum],'amount':['sum'] ,'PayerId':['count']})
client.columns = client.columns.droplevel(level=1)
client = client.rename(columns={'PayerId':'freq'}).reset_index()
client['Ind_fina'] = client.amountfinancedByXepelin/client.amount
client.sort_values(by=['amount'], inplace=False,ascending=False)
client
| PayerId | amountfinancedByXepelin | amount | freq | Ind_fina | |
|---|---|---|---|---|---|
| 0 | 1 | 10523.52 | 17755.02 | 6 | 0.592707 |
| 1 | 2 | 0.00 | 13678.01 | 3 | 0.000000 |
| 2 | 4 | 0.00 | 4002.12 | 3 | 0.000000 |
| 3 | 5 | 8303.25 | 28427.60 | 3 | 0.292084 |
| 4 | 6 | 0.00 | 75813.92 | 4 | 0.000000 |
| ... | ... | ... | ... | ... | ... |
| 193 | 196 | 3782.25 | 28298.36 | 4 | 0.133656 |
| 194 | 197 | 222388.12 | 225552.18 | 4 | 0.985972 |
| 195 | 198 | 1000.01 | 6658.93 | 4 | 0.150176 |
| 196 | 199 | 2591.48 | 11314.36 | 5 | 0.229043 |
| 197 | 200 | 0.00 | 2223.52 | 2 | 0.000000 |
198 rows × 5 columns
fig = px.bar(client, x = 'PayerId', y = 'freq')
fig.show()
La siguiente grafica me ayuda a entender cuantas personas nuevas o que no se encuentran en los historicos estoy recibiendo por mes esto en analisis posteriores me ayuda a revisar su antiguedad y su nivel de crecimiento
# Clientes nuevos por mes
mes = ['2021-02','2021-03','2021-04','2021-05','2021-06','2021-07','2021-08','2021-09']
acumulado = []
nuevos_id = []
for i in mes:
new = df[df['mes_id'] == i]
nuevas = new.PayerId.unique()
conteo = 0
for j in nuevas:
if j not in acumulado:
conteo += 1
acumulado += j ,
nuevos_id += conteo ,
fig = go.Figure()
fig.add_trace(go.Scatter(x=mes, y=nuevos_id,
mode='lines',
name='Porcentaje de Financiacion' ),
)
fig.update_layout(
title_text="Clientes nuevos mes"
)
fig.show()
El siguiente analisis busca encontrar con los comportamientos que estoy teniendo cual seria los montos esperados para el mes de Octubre -2021 y su financiación.
Hay un reto y es que los datos que tenemos en la actualidad son escasos y cada vez es menos el nivel de clientes nuevos que ayuden a apalancar las decisiones de financiación.
Existen muchas formas de abordar los datos aproximandonos desde una serie de tiempo:
a. Utilizando los modelos autoregresivos de series de tiempo
b. Utilizando modelos de redes neuronales de series de tiempo
c. Forecasting autorregresivo recursivo usando randonForest
Inicialmente una prediccion natural se daria con el planteamiento de una serie de tiempo debido al orden natural de fechas
# Se realiza un acumulado de transacciones por dia y se revisa el monto transado
serie = df.groupby(['paidAt','mes_id'])['amount'].sum().reset_index()
fig = px.line(serie, x='paidAt', y="amount")
fig.show()
serie.head(5)
| paidAt | mes_id | amount | |
|---|---|---|---|
| 0 | 2021-02-08 | 2021-02 | 2410.72 |
| 1 | 2021-02-15 | 2021-02 | 4035.26 |
| 2 | 2021-02-18 | 2021-02 | 27979.20 |
| 3 | 2021-03-02 | 2021-03 | 1477.46 |
| 4 | 2021-03-04 | 2021-03 | 8688.40 |
Generamos primeramente un modelo autorregresivo con el proposito de proyectar fechas del mes de octubre pero el resultado es el esperado.
serie = df.groupby(['paidAt','mes_id'])['amount'].sum().reset_index()
serie = serie.sort_values(by=['paidAt'], inplace=False,ascending=True)
serie.index = serie['paidAt']
del serie['paidAt']
del serie['mes_id']
erie = serie.asfreq('d')
serie = serie.fillna(method='ffill')
pronostico = 15
particion = (len(serie)-pronostico)/len(serie)
size = int(len(serie)*particion)
df2, df2_test = serie.iloc[:size], serie.iloc[size:]
start_date = str(df2.index[-1] + timedelta(days=1))
end_date = str(df2_test.index[-1])
from pmdarima.arima import auto_arima
model_auto = auto_arima(df2.amount)
model_auto.summary()
df_auto_pred = pd.DataFrame(model_auto.predict(n_periods = len(df2_test[start_date:end_date]),
index = df2_test[start_date:start_date].index))
import plotly.graph_objects as go
fig = go.Figure()
# Grafico
fig.add_trace(go.Scatter(x=df2_test.index, y=df2_test.amount,
mode='lines',
name='Real'))
fig.add_trace(go.Scatter(x=df2_test.index, y=df_auto_pred.iloc[:, 0], name='Prediccion',
line = dict(color='firebrick', width=4, dash='dot')))
fig.show()
d:\usuarios\wilmagju\appdata\local\programs\python\python38\lib\site-packages\statsmodels\tsa\base\tsa_model.py:7: FutureWarning: pandas.Int64Index is deprecated and will be removed from pandas in a future version. Use pandas.Index with the appropriate dtype instead. d:\usuarios\wilmagju\appdata\local\programs\python\python38\lib\site-packages\statsmodels\tsa\base\tsa_model.py:7: FutureWarning: pandas.Float64Index is deprecated and will be removed from pandas in a future version. Use pandas.Index with the appropriate dtype instead.
El resultado es el esperado al tener demasiado dias con datos faltantes y pocos dias mas medir la estacionalidad los modelos autorregresivos no son un estimador para nuestra caso. Aunque pueden ayudar a estimar lineas de tendiencias como lo vemos en la grafica
Decidimos entonces estimar el mes de octubre como una tendencia mensual de los datos del pasado para eso agrupamos los datos.
from prophet import Prophet
#
serie = df.groupby(['mes_id'])['amount'].sum().reset_index()
fig = px.line(serie, x='mes_id', y="amount")
fig.show()
serie
| mes_id | amount | |
|---|---|---|
| 0 | 2021-02 | 34425.18 |
| 1 | 2021-03 | 269826.65 |
| 2 | 2021-04 | 753946.49 |
| 3 | 2021-05 | 7250723.28 |
| 4 | 2021-06 | 7132982.75 |
| 5 | 2021-07 | 5765920.25 |
| 6 | 2021-08 | 5044795.41 |
| 7 | 2021-09 | 1178392.90 |
# Los datos de los dos primeros meses me pueden afectar la tendencia
serie = serie[serie.mes_id != '2021-02']
serie = serie[serie.mes_id != '2021-03']
serie.rename(columns = {'mes_id':'ds', 'amount':'y'}, inplace = True)
m = Prophet()
m.fit(serie)
09:57:08 - cmdstanpy - INFO - Chain [1] start processing 09:57:08 - cmdstanpy - INFO - Chain [1] done processing
<prophet.forecaster.Prophet at 0x1be7f2ec9d0>
# Predigo un perído de la tendencia hace adelante
future = m.make_future_dataframe(periods = 1)
future.iloc[6] = '2021-10-01 00:00:00'
forecast = m.predict(future)
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']]
| ds | yhat | yhat_lower | yhat_upper | |
|---|---|---|---|---|
| 0 | 2021-04-01 | 4.938402e+06 | 1.652943e+06 | 7.986045e+06 |
| 1 | 2021-05-01 | 4.772459e+06 | 1.408518e+06 | 8.002580e+06 |
| 2 | 2021-06-01 | 4.600984e+06 | 1.366823e+06 | 7.717158e+06 |
| 3 | 2021-07-01 | 4.435041e+06 | 1.174517e+06 | 7.803715e+06 |
| 4 | 2021-08-01 | 4.263566e+06 | 1.257823e+06 | 7.601198e+06 |
| 5 | 2021-09-01 | 4.092091e+06 | 1.071089e+06 | 7.415747e+06 |
| 6 | 2021-10-01 | 3.926147e+06 | 7.414822e+05 | 6.946372e+06 |
from prophet.plot import plot_plotly, plot_components_plotly
plot_plotly(m, forecast)
En los datos mensualizados observamos el mismo resultado observamos una tendencia de montos esperada con la cual podemos decir que los montos totales en la plataforma son:
serie = df.groupby(['mes_id'])['amount'].sum().reset_index()
y = []
for i in serie.amount:
y += i,
y += 3926147.223685805,
colors = ['lightslategray',] * 9
colors[8] = 'crimson'
fig = go.Figure(data=[go.Bar(
x=['2021-02','2021-03','2021-04', '2021-05', '2021-06', '2021-07', '2021-08', '2021-09','2021-10'],
y=y,
marker_color=colors # marker color can be a single color value or an iterable
)])
fig.update_layout(title_text='Prediccion mes octubre')
Hay cierto tipo de informacion que me pudiera resultar relevante a la hora de diseñar un producto financiero o trata de predecir el comportamiento de las empresas y su financiacion estas podrian ser
a. Informacion del sector, generar una clasificacion demografica de donde es la empresa y a que sector pertenece por ejemplo:
salud , transporte , manufactura, contruccion etc esto nos permitiria generar variables exogenas al modelo
b. Informacion de los estados financieros y el cupo disponible maximo a financiar por xepelin, esto nos permitiria evidenciar el verdero potencial de crecimiento del negocio y al conocer como seria el uso financiero del servicio
c. Tasas de interes, plazos y estado de pago , me ayudaria a entender el habito de pago de las empresas y el rendimiento para crear un modelo de negocio en punto de equilibrio
Es un modelo simple de implementar que esta basado en la tendencia, elementos de estacionalidad elementos de ruido blanco que a veces no dejan decifrar con claridad se basa en la originalidad y el buen diseño del producto para generar valor en las empresas en el mercado
Tenemos que no tiene la informacion suficiente y variables exogenas para generar un modelo mas acertado, en el futuro se podrian generar curvas por negocio no basada en totales generando un bucle predictivo por empresa que permita ayudarle a tomar las mejores decisiones sobre las recurrencia en la financiación
Una opcion es generar un modelo estadisticos basado en la observacion y el conocimiento financieron de los datos , estos nos permitiria no solo predecir como en el modelo anterior sino tambien adelantarnos en implementar politicas mas disruptivas que que puedan absorver mas el % de financiacion como la base del negocio.
Esto se podia lograr implementando medidas de educación financiera y de fidelizacion de empresar al ofrecer un sistema de gestion de factura potente que puede apalacar facilemente sus desciciones de negocio
Por ejemplo el siguiente modelo busca generar una clasificacion intuitiva que nos permita generar facilmente un sistema de recomendacion o un sistema de clasificacion que permita adaptar soluciones a cada negocio y un mayor comprendimiento de los datos
# Generamos una clasificacion en los clientes segun la frecuencia y/o recurrencia en su financiación
conditions = [
(client['freq'] <= 2),
(client['freq'] > 2) & (client['freq'] <= 4),
(client['freq'] > 4) & (client['freq'] <= 8),
(client['freq'] > 8)
]
values = ['A', 'B', 'C', 'D']
client['clase_frecuencia'] = np.select(conditions, values)
# Luego generamos una condiciones segun ha sido un historico en los historicos de fianciacion
conditions2 = [
(client['Ind_fina'] <= 0.1),
(client['Ind_fina'] > 0.1) & (client['Ind_fina'] <= 0.3),
(client['Ind_fina'] > 0.3) & (client['Ind_fina'] <= 0.5),
(client['Ind_fina'] > 0.5)
]
client['clase_financiacion'] = np.select(conditions2, values)
# Luego generaramos un datos segun los montos que estan teniendo
conditions3 = [
(client['amount'] <= 0.1),
(client['amount'] > 0.1) & (client['amount'] <= 0.3),
(client['amount'] > 0.3) & (client['amount'] <= 0.5),
(client['amount'] > 0.5)
]
client['clase_monto'] = np.select(conditions2, values)
client
| PayerId | amountfinancedByXepelin | amount | freq | Ind_fina | clase_frecuencia | clase_financiacion | clase_monto | |
|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 10523.52 | 17755.02 | 6 | 0.592707 | C | D | D |
| 1 | 2 | 0.00 | 13678.01 | 3 | 0.000000 | B | A | A |
| 2 | 4 | 0.00 | 4002.12 | 3 | 0.000000 | B | A | A |
| 3 | 5 | 8303.25 | 28427.60 | 3 | 0.292084 | B | B | B |
| 4 | 6 | 0.00 | 75813.92 | 4 | 0.000000 | B | A | A |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 193 | 196 | 3782.25 | 28298.36 | 4 | 0.133656 | B | B | B |
| 194 | 197 | 222388.12 | 225552.18 | 4 | 0.985972 | B | D | D |
| 195 | 198 | 1000.01 | 6658.93 | 4 | 0.150176 | B | B | B |
| 196 | 199 | 2591.48 | 11314.36 | 5 | 0.229043 | C | B | B |
| 197 | 200 | 0.00 | 2223.52 | 2 | 0.000000 | A | A | A |
198 rows × 8 columns
Esto nos ofrece unicialmente una analis rapido de la empresa en tres dimensiones su frecuencia, el monto que está transando en plataforma y el % que financia asi si tenemos por ejemplo una empresa 'DDDD' es una empresa que tiene gran recurrencia en sus facturas, tiene gran margen de financiacion y maneja buen volumen. La plataforma deberia emplear soluciones diferentes y personalizadas segun cada tipo de segmento y potencializarla